package com.xnyzc.lhy.common.util.sign;

import com.xnyzc.lhy.common.component.didi.DiDiUrlConstant;
import com.xnyzc.lhy.common.util.MD5Util;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

/**
 * 认证工具
 */
@Component
public class AttestationTool {

    /**
     * 是否开启验签
     */
    private static boolean isSignCheck;

    @Value("${global.pang.didiSignCheck}")
    public void setIsSignCheck(boolean didiSignCheck) {
        isSignCheck = didiSignCheck;
    }

    /**
     * 使用私钥加密
     */
    public String encryptByPrivateKey(String data) {
        String str = "";
        try {
            data = "1234";
            String priKey = readStringPrivateFile();
            // String priKey = DiDiUrlConstant.SIGN_PRO;
            byte[] pribyte = base64decode(priKey.trim());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pribyte);
            KeyFactory fac = KeyFactory.getInstance("RSA");
            RSAPrivateKey privateKey = (RSAPrivateKey) fac.generatePrivate(keySpec);
            Cipher c1 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            c1.init(Cipher.ENCRYPT_MODE, privateKey);
            str = base64encode(c1.doFinal(data.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();

        }
        return str;
    }

    public static byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }

    /**
     * 使用私钥解密
     */
    public String decryptByPrivateKey(String data) {
        String str = "";
        try {
            String priKey = readStringPrivateFile();
            byte[] pribyte = base64decode(priKey.trim());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pribyte);
            KeyFactory fac = KeyFactory.getInstance("RSA");
            RSAPrivateKey privateKey = (RSAPrivateKey) fac.generatePrivate(keySpec);
            Cipher c1 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            c1.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] temp = c1.doFinal(base64decode(data));
            str = new String(temp);
        } catch (Exception e) {
            e.printStackTrace();

        }
        return str;
    }


    /**
     * 使用公钥加密
     */
    public String encryptByPublicKey(String data) {
        String str = "";
        try {
            String pubKey = readStringPublicFile();
            byte[] pubbyte = base64decode(pubKey.trim());
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubbyte);
            KeyFactory fac = KeyFactory.getInstance("RSA");
            RSAPublicKey rsaPubKey = (RSAPublicKey) fac.generatePublic(keySpec);
            Cipher c1 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            c1.init(Cipher.ENCRYPT_MODE, rsaPubKey);
            str = base64encode(c1.doFinal(data.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();

        }
        return str;
    }

    /**
     * 使用公钥解密
     */
    public String decryptByPublicKey(String data) {
        String str = "";
        try {
            String pubKey = readStringPublicFile();
            byte[] pubbyte = base64decode(pubKey.trim());
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubbyte);
            KeyFactory fac = KeyFactory.getInstance("RSA");
            RSAPublicKey rsaPubKey = (RSAPublicKey) fac.generatePublic(keySpec);
            Cipher c1 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            c1.init(Cipher.DECRYPT_MODE, rsaPubKey);
            byte[] temp = c1.doFinal(base64decode(data));
            str = new String(temp);
        } catch (Exception e) {
            e.printStackTrace();

        }
        return str;
    }

    /**
     * 本方法使用SHA1withRSA签名算法产生签名
     *
     * @return String 签名的返回结果(16进制编码)。当产生签名出错的时候，返回null。
     */
    public String signByPrivateKey(String src, String priKey) {
        try {
            Signature sigEng = Signature.getInstance("SHA1withRSA");
            //String priKey = new String(readStringPrivateFile());
            byte[] pribyte = base64decode(priKey.trim());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pribyte);
            KeyFactory fac = KeyFactory.getInstance("RSA");
            RSAPrivateKey privateKey = (RSAPrivateKey) fac.generatePrivate(keySpec);
            sigEng.initSign(privateKey);
            sigEng.update(src.getBytes());
            byte[] signature = sigEng.sign();
            return base64encode(signature);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 使用共钥验证签名
     *
     * @param sign
     * @param src
     * @return
     */
    public boolean verifyByPublicKey(String sign, String src, String pubKey) {
        try {
            Signature sigEng = Signature.getInstance("SHA1withRSA");
            byte[] pubbyte = base64decode(pubKey.trim());
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubbyte);
            KeyFactory fac = KeyFactory.getInstance("RSA");
            RSAPublicKey rsaPubKey = (RSAPublicKey) fac.generatePublic(keySpec);
            sigEng.initVerify(rsaPubKey);
            sigEng.update(src.getBytes());
            byte[] sign1 = base64decode(sign);
            return sigEng.verify(sign1);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * base64加密
     *
     * @param bstr
     * @return
     */
    @SuppressWarnings("restriction")
    private static String base64encode(byte[] bstr) {
        String str = new sun.misc.BASE64Encoder().encode(bstr);
        str = str.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
        return str;
    }

    /**
     * base64解密
     *
     * @param str
     * @return byte[]
     */
    @SuppressWarnings("restriction")
    private static byte[] base64decode(String str) {
        byte[] bt = null;
        try {
            sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
            bt = decoder.decodeBuffer(str);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bt;
    }

    /**
     * 获取文件内容
     *
     * @return
     */
    private String readStringPrivateFile() {
        StringBuffer str = new StringBuffer();
        String fileName = "D:\\Lhy\\闪电出行项目\\相关文档\\闪电出行项目开发需求内容\\java_public.pem";
        try {
            File file = new File(fileName);
            FileReader fr = new FileReader(file);
            char[] temp = new char[1024];
            while (fr.read(temp) != -1) {
                str.append(temp);
            }
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();

        }
        return str.toString();
    }

    /**
     * 获取文件内容
     *
     * @return
     */
    private String readStringPublicFile() {
        InputStream privateKeyPomPath = Thread.currentThread().getContextClassLoader().getResourceAsStream("config/java_public.pem");
        byte[] getData = new byte[0];
        try {
            getData = readInputStream(privateKeyPomPath);
            privateKeyPomPath.read(getData);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(getData);
    }

    /**
     * 传入参数加签
     *
     * @param t
     * @param <T>
     * @return
     */
    public static <T> String getSign(T t, String paramFormat) {
        Map<String, Object> resMap = new TreeMap<>(new Comparator<String>() {
            @Override
            public int compare(String str1, String str2) {
                return str1.compareTo(str2);
            }
        });

        try {
            Map<String, Object> map = BeanUtils.describe(t);
            map.remove("class");
            resMap.putAll(map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        String sortString = "";
        String sortStringSign = "";
        for (Object key : resMap.keySet()) {
            if (key.equals("sign") || key.equals("multi_location") || resMap.get(key) == null || "null".equals(resMap.get(key))) {
                continue;
            }
            // URLEncoder.encode(String, "UTF-8") 中文编码
            try {
                if (key.equals("passenger_bill")) {
                    sortString = sortString + key + "=" + URLEncoder.encode(resMap.get(key).toString(), "UTF-8") + "&";
                    sortStringSign = sortStringSign + key + "=" + resMap.get(key).toString() + "&";
                } else {
                    sortString = sortString + key + "=" + URLEncoder.encode(resMap.get(key).toString(), "UTF-8") + "&";
                    sortStringSign = sortStringSign + key + "=" + resMap.get(key) + "&";
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        sortString = sortString.substring(0, sortString.length() - 1);
        sortStringSign = sortStringSign.substring(0, sortStringSign.length() - 1);
        //开始使用各种加解密（签）方法
        AttestationTool rsa = new AttestationTool();
        // String str = sortString;
        String priKey = DiDiUrlConstant.didiSignPro;
        String sign = rsa.signByPrivateKey(sortStringSign, priKey);
        String result = null;
        try {
            if (StringUtils.equals(DiDiUrlConstant.X_WWW_FORM_URLENCODED, paramFormat)) {
                result = sortString + "&sign=" + URLEncoder.encode(sign, "UTF-8");
            }
            if (StringUtils.equals(DiDiUrlConstant.JSON_PARAM, paramFormat)) {
                result = sortStringSign + "&sign=" + sign;
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // result = result.replace("+", "%2B");
        return result;
    }

    /**
     * 接收参数验签
     *
     * @param t
     * @param <T>
     * @return
     */
    public static <T> boolean getRequestSign(T t, String signOld, String priKey) {
        if (!isSignCheck) {
            return true;
        }
        AttestationTool rsa = new AttestationTool();
        String sortStringSign = rsa.getSortRequestStr(t);
        //开始使用各种加解密（签）方法
        return rsa.verifyByPublicKey(signOld, sortStringSign, priKey);
    }

    /**
     * 获取排序后的 用&拼接的字符串
     * @param t
     * @param <T>
     * @return
     */
    public static <T> String getSortRequestStr(T t){
        Map<String, Object> resMap = new TreeMap<>(new Comparator<String>() {
            @Override
            public int compare(String str1, String str2) {
                return str1.compareTo(str2);
            }
        });

        try {
            Map<String, Object> map = BeanUtils.describe(t);
            map.remove("class");
            resMap.putAll(map);
        } catch (Exception e) {
            e.printStackTrace();
        }

        String sortStringSign = "";
        for (Object key : resMap.keySet()) {
            if (key.equals("sign") || key.equals("multi_location") || resMap.get(key) == null || "null".equals(resMap.get(key)) || "".equals(resMap.get(key))) {
                continue;
            }
            if (key.equals("passenger_bill")) {
                sortStringSign = sortStringSign + key + "=" + resMap.get(key).toString() + "&";
            } else {
                sortStringSign = sortStringSign + key + "=" + resMap.get(key) + "&";
            }
        }
        sortStringSign = sortStringSign.substring(0, sortStringSign.length() - 1);
        return sortStringSign;
    }

    /**
     * 接收参数验签MD5
     *
     * @param t
     * @param <T>
     * @return
     */
    public static <T> boolean checkRequestSignMD5(T t, String signOld, String priKey) {
//        if (!isSignCheck)  return true;
        AttestationTool rsa = new AttestationTool();
        String sortRequestStr = rsa.getSortRequestStr(t);
        //开始使用各种加解密（签）方法
        String md5Str = MD5Util.MD5(sortRequestStr+"&"+priKey);
        return md5Str.equals(signOld);
    }
}
